package io.github.echarts.model.coordinate;

/**
 * 数据范围类，表示一组数据的最小值和最大值
 * 用于坐标系计算坐标映射
 */
public class DataRange {
    /**
     * 数据最小值
     */
    private double min;
    
    /**
     * 数据最大值
     */
    private double max;
    
    /**
     * 是否包含零点
     */
    private boolean containsZero;
    
    /**
     * 是否有负值
     */
    private boolean hasNegative;
    
    /**
     * 是否是固定范围（用户指定）
     */
    private boolean fixed;
    
    /**
     * 创建数据范围
     * 
     * @param min 最小值
     * @param max 最大值
     */
    public DataRange(double min, double max) {
        this.min = min;
        this.max = max;
        this.containsZero = min <= 0 && max >= 0;
        this.hasNegative = min < 0;
        this.fixed = false;
    }
    
    /**
     * 创建一个空数据范围
     */
    public DataRange() {
        this(0, 0);
    }
    
    /**
     * 获取最小值
     * 
     * @return 最小值
     */
    public double getMin() {
        return min;
    }
    
    /**
     * 设置最小值
     * 
     * @param min 最小值
     */
    public void setMin(double min) {
        this.min = min;
        this.containsZero = min <= 0 && max >= 0;
        this.hasNegative = min < 0;
    }
    
    /**
     * 获取最大值
     * 
     * @return 最大值
     */
    public double getMax() {
        return max;
    }
    
    /**
     * 设置最大值
     * 
     * @param max 最大值
     */
    public void setMax(double max) {
        this.max = max;
        this.containsZero = min <= 0 && max >= 0;
    }
    
    /**
     * 获取数据范围大小
     * 
     * @return 最大值与最小值的差
     */
    public double getSpan() {
        return max - min;
    }
    
    /**
     * 判断范围是否为零（最小值等于最大值）
     * 
     * @return 如果范围为零，则返回true
     */
    public boolean isZero() {
        return Double.compare(min, max) == 0;
    }
    
    /**
     * 判断数据范围是否包含零点
     * 
     * @return 如果包含零点，则返回true
     */
    public boolean containsZero() {
        return containsZero;
    }
    
    /**
     * 判断数据范围是否有负值
     * 
     * @return 如果有负值，则返回true
     */
    public boolean hasNegative() {
        return hasNegative;
    }
    
    /**
     * 判断数据范围是否固定（用户指定）
     * 
     * @return 如果是固定范围，则返回true
     */
    public boolean isFixed() {
        return fixed;
    }
    
    /**
     * 设置数据范围是否固定
     * 
     * @param fixed 是否固定
     */
    public void setFixed(boolean fixed) {
        this.fixed = fixed;
    }
    
    /**
     * 合并两个数据范围，取两者的最大覆盖范围
     * 
     * @param other 另一个数据范围
     * @return 合并后的数据范围
     */
    public DataRange merge(DataRange other) {
        if (other == null) {
            return this;
        }
        
        double newMin = Math.min(this.min, other.min);
        double newMax = Math.max(this.max, other.max);
        
        return new DataRange(newMin, newMax);
    }
    
    /**
     * 调整数据范围，确保范围的合理性
     * 如果最小值等于最大值，会做适当扩展
     * 
     * @return 调整后的数据范围
     */
    public DataRange normalize() {
        if (Double.compare(min, max) == 0) {
            // 处理min=max的情况，避免除零错误
            if (min == 0) {
                // 如果都是0，扩展到-1到1
                min = -1;
                max = 1;
            } else {
                // 否则，扩展10%
                double delta = Math.abs(min * 0.1);
                min -= delta;
                max += delta;
            }
        }
        return this;
    }
    
    /**
     * 计算值在范围内的归一化位置（0-1之间）
     * 
     * @param value 要计算的值
     * @return 归一化后的位置（0-1之间）
     */
    public double normalize(double value) {
        if (isZero()) {
            return 0.5; // 避免除零错误
        }
        return (value - min) / (max - min);
    }
    
    /**
     * 将归一化值（0-1之间）转换回实际数据值
     * 
     * @param ratio 归一化值（0-1之间）
     * @return 实际数据值
     */
    public double denormalize(double ratio) {
        return min + ratio * (max - min);
    }
    
    @Override
    public String toString() {
        return "DataRange{" +
                "min=" + min +
                ", max=" + max +
                ", containsZero=" + containsZero +
                ", hasNegative=" + hasNegative +
                '}';
    }
} 